#!/usr/bin/env bash
set -euo pipefail

PROGNAME=$(basename "$0")

usage() {
  cat <<EOF
Usage: $PROGNAME [options]

Options:
  -u, --username USERNAME   Identify user by username
  -e, --email EMAIL         Identify user by email
  -p, --password PASSWORD   Password to set (if omitted, will prompt)
  -c, --cost COST           BCrypt cost factor (default: 12)
      --apply               Apply the SQL to the database (requires psql or docker-compose)
      --psql-cmd CMD        Command to execute psql, e.g. "docker-compose exec postgres psql -U postgres -d aiplatform -c"
  -h, --help                Show this help

This tool generates a BCrypt password hash and prints a psql-compatible UPDATE
statement to set the password for a user (by username or email). By default it
only prints the SQL. Use --apply to run it (script will try to run with psql or
docker-compose if available).

Requirements (one of):
  - htpasswd (with -B support)  OR
  - python3 with 'bcrypt' module  OR
  - docker installed (will use python:3 image to generate hash)

EOF
}

if [ $# -eq 0 ]; then
  usage
  exit 0
fi

USERNAME=""
EMAIL=""
PASSWORD=""
COST=12
APPLY=0
ASSUME_YES=0
PSQL_CMD=""
OUTPUT_FILE=""

while [[ $# -gt 0 ]]; do
  key="$1"
  case $key in
    -u|--username)
      USERNAME="$2"; shift 2;;
    -e|--email)
      EMAIL="$2"; shift 2;;
    -p|--password)
      PASSWORD="$2"; shift 2;;
    -c|--cost)
      COST="$2"; shift 2;;
    --apply)
      APPLY=1; shift;;
    --psql-cmd)
      PSQL_CMD="$2"; shift 2;;
    -o|--out)
      OUTPUT_FILE="$2"; shift 2;;
    --yes)
      ASSUME_YES=1; shift;;
    -h|--help)
      usage; exit 0;;
    *)
      echo "Unknown option: $1" >&2; usage; exit 2;;
  esac
done

if [ -z "$USERNAME" ] && [ -z "$EMAIL" ]; then
  echo "You must provide either --username or --email" >&2
  exit 2
fi

if [ -z "$PASSWORD" ]; then
  # prompt for password silently
  read -s -p "Password: " PASSWORD
  echo
fi

generate_with_htpasswd() {
  if command -v htpasswd >/dev/null 2>&1; then
    # test if htpasswd supports -B
    if htpasswd -B -nb testuser testpass >/dev/null 2>&1; then
      # use -nbB to print username:hash
      local out
      out=$(htpasswd -nbB "dummy" "$PASSWORD" 2>/dev/null) || return 1
      # out is dummy:$2y$...
      echo "$out" | cut -d: -f2
      return 0
    fi
  fi
  return 1
}

generate_with_python() {
  if command -v python3 >/dev/null 2>&1; then
    # Use python bcrypt by piping password via stdin and passing cost in env
    BCRYPT_COST="$COST" echo -n "$PASSWORD" | python3 -c '
import sys,os
try:
    import bcrypt
except Exception:
    sys.exit(2)
cost=int(os.environ.get("BCRYPT_COST","12"))
pw=sys.stdin.read().strip()
print(bcrypt.hashpw(pw.encode("utf-8"), bcrypt.gensalt(rounds=cost)).decode())
'
    return $?
  fi
  return 1
}

generate_with_docker() {
  if command -v docker >/dev/null 2>&1; then
    echo -n "$PASSWORD" | docker run --rm -i -e BCRYPT_COST="$COST" python:3 python -c '
import sys,os
import bcrypt
cost=int(os.environ.get("BCRYPT_COST","12"))
pw=sys.stdin.read().strip()
print(bcrypt.hashpw(pw.encode("utf-8"), bcrypt.gensalt(rounds=cost)).decode())
'
    return $?
  fi
  return 1
}


hash=""

echo "Generating bcrypt hash (cost=${COST})..." >&2

if hash=$(generate_with_htpasswd 2>/dev/null); then
  echo "(generated via htpasswd)" >&2
elif hash=$(generate_with_python 2>/dev/null); then
  echo "(generated via python3 bcrypt)" >&2
elif hash=$(generate_with_docker 2>/dev/null); then
  echo "(generated via docker python image)" >&2
else
  cat <<MSG >&2
Could not generate bcrypt hash: no supported method found.
Install one of:
  - htpasswd (apache2-utils) with bcrypt support (htpasswd -B)
  - python3 and 'bcrypt' module (pip install bcrypt)
  - docker (will use python:3 image)

Alternatively use the existing Node tool: backend/tools/gen-bcrypt.js
MSG
  exit 3
fi

if [ -n "$USERNAME" ]; then
  identifier_sql="\"Username\" = '$(printf "%s" "$USERNAME" | sed "s/'/''/g")'"
else
  identifier_sql="\"Email\" = '$(printf "%s" "$EMAIL" | sed "s/'/''/g")'"
fi

# Escape single quotes in the hash for SQL literal
escaped_hash=$(printf "%s" "$hash" | sed "s/'/''/g")

# Build SQL safely
sql=$(printf 'BEGIN;\nUPDATE "Users" SET "PasswordHash" = '\''%s'\'' WHERE %s;\nCOMMIT;\n' "$escaped_hash" "$identifier_sql")

echo
echo "-- SQL to run against the aiplatform DB:" >&2
echo
echo "$sql"

# If the user requested output to a file, write it
if [ -n "$OUTPUT_FILE" ]; then
  if [ -e "$OUTPUT_FILE" ] && [ $ASSUME_YES -ne 1 ]; then
    read -p "Output file $OUTPUT_FILE exists — overwrite? Type 'yes' to confirm: " confirm_out
    if [ "$confirm_out" != "yes" ]; then
      echo "Aborted by user (will not overwrite $OUTPUT_FILE)." >&2
      exit 1
    fi
  fi
  printf "%s\n" "$sql" > "$OUTPUT_FILE"
  chmod 600 "$OUTPUT_FILE" || true
  echo "Wrote SQL to $OUTPUT_FILE" >&2
fi

if [ $APPLY -eq 1 ]; then
  echo >&2
  echo >&2
  echo "-- Applying to database..." >&2

  if [ $ASSUME_YES -ne 1 ]; then
    read -p "Are you sure you want to apply this change to the database? Type 'yes' to confirm: " confirm
    if [ "$confirm" != "yes" ]; then
      echo "Aborted by user." >&2
      exit 1
    fi
  fi

  # if a custom psql command was provided, use it
  if [ -n "$PSQL_CMD" ]; then
    # Run the provided command and pipe the SQL into it. The provided command should read SQL from stdin.
    printf "%s\n" "$sql" | eval "$PSQL_CMD"
    exit $?
  fi

  if command -v psql >/dev/null 2>&1; then
    # Use environment variables for PG connection (PGHOST, PGPORT, PGDATABASE, PGUSER, PGPASSWORD)
    printf "%s\n" "$sql" | psql
    exit $?
  fi

  if command -v docker >/dev/null 2>&1 && command -v docker-compose >/dev/null 2>&1; then
    # Try docker-compose exec postgres psql -U postgres -d aiplatform
    echo "psql not found, attempting to run via docker-compose exec -T postgres psql..." >&2
    printf "%s\n" "$sql" | docker-compose exec -T postgres psql -U postgres -d aiplatform
    exit $?
  fi

  echo "Cannot apply SQL: neither psql nor docker-compose available. Run the SQL manually." >&2
  exit 4
fi

exit 0
